#include "CustomCnvToImgAlgorithm.h"

CustomCnvToImgAlgorithmPlugin::CustomCnvToImgAlgorithmPlugin(QObject *parent)
    :QObject(parent)
{
    m_settingManagerPtr=nullptr;
    m_VarSetManagerPtr=nullptr;
}

CustomCnvToImgAlgorithmPlugin::~CustomCnvToImgAlgorithmPlugin()
{
    FreeRawImageData(m_recvImgData);
    FreeRawImageData(m_resultImgData);
}

void CustomCnvToImgAlgorithmPlugin::setSettingManagerPtr(SettingManager *ptr)
{
    m_settingManagerPtr=ptr;
}

void CustomCnvToImgAlgorithmPlugin::setVarSetManagerPtr(VarSetManager *ptr)
{
    m_VarSetManagerPtr=ptr;
}

void CustomCnvToImgAlgorithmPlugin::initByGUIThread()
{
    /*
    //
    //这个函数是在登录界面显示前调用的，通常在这里进行一些Qt界面的初始化操作
    //例如，使用 m_settingManagerPtr->prependPageToSettingDialog() 在设置对话框中添加参数选项卡
    //例如，使用 m_VarSetManagerPtr->insertPageToTuneDialog() 在调整对话框中添加变量选项卡
    //
    */

    if(m_settingManagerPtr){
        checkParamters();

        connect(this,SIGNAL(toastInfo(QString,QString)),
                m_settingManagerPtr,SLOT(toastInfo(QString,QString))
                );
    }

    initCudaAccLib();
}

void CustomCnvToImgAlgorithmPlugin::initContext()
{
    /*
    //
    //这个函数是在登录界面显示时非阻塞调用的，
    //通常在这里进行一些比较耗时的初始化操作，
    //当这个函数返回后，登录界面的打开按钮才显示
    //
    */
}

QObject *CustomCnvToImgAlgorithmPlugin::obj()
{
    return this;
}

void CustomCnvToImgAlgorithmPlugin::resetInterlBuffer(int cols, int rows, int type)
{
    /*
    //
    //这个函数是在采集卡需要重新初始化后 调用的
    //请在这里根据cols,rows,type 变量， 重新初始化本工程需要用到的内存
    //
    */

    if(type!=BUF_DATA_TYPE_U16C1
            || cols<1
            || rows<1
            ){
        return;
    }
    FreeRawImageData(m_recvImgData);
    AllocateRawImageDataMem(m_recvImgData,BUF_DATA_TYPE_U16C1,
                            cols,rows
                            );

    FreeRawImageData(m_resultImgData);
    AllocateRawImageDataMem(m_resultImgData,BUF_DATA_TYPE_U16C1,
                            rows,cols/2
                            );

}

void CustomCnvToImgAlgorithmPlugin::receiveInputData(const RAW_IMAGE_DATA &data)
{
    /*
    //
    //这个函数是在帧产生线程中循环调用的
    //每当产生一帧新的采集卡原始波形帧数据，该函数被调用一次
    //请在这里将接收到的数据 data 复制到 本工程的 内存缓冲区
    //
    */

    if(data.cols==m_recvImgData.cols
            && data.rows==m_recvImgData.rows
            && data.type==m_recvImgData.type
            && data.totalByte==m_recvImgData.totalByte
            ){
        StdMemCpy(m_recvImgData.ptr,data.ptr,m_recvImgData.totalByte);
    }
}

void CustomCnvToImgAlgorithmPlugin::getOutputDataShadowCopy(RAW_IMAGE_DATA &dst)
{
    /*
    //
    //这个函数是在帧产生线程中循环调用的
    //帧产生线程通过这个函数获取本工程的处理结果
    //处理结果是16位单通道无符号数图像数据，
    //处理结果图像应保证高度方向为 采集卡的采样方向
    //必须把处理结果的行、列、类形、指向的内存位置、占用的内存大小  放到 结构变量 dst中
    //该函数在execAlgorithmProcess()之后被调用
    //调用本函数的 帧产生线程不会释放处理结果的内存
    //
    */

    dst.type=m_resultImgData.type;
    dst.cols=m_resultImgData.cols;
    dst.rows=m_resultImgData.rows;
    dst.totalByte=m_resultImgData.totalByte;
    dst.ptr=m_resultImgData.ptr;
}

void CustomCnvToImgAlgorithmPlugin::onAppAccountLevelChanged(int nowlevel)
{
    Q_UNUSED(nowlevel);
}

void CustomCnvToImgAlgorithmPlugin::execAlgorithmProcess()
{
    /*
    //
    //这个函数是在帧产生线程中循环调用的
    //该函数在receiveInputData()之后被调用
    //请在这里进行算法处理
    //
    */

//    if(getIsCudaAccLibOK()){
//        processByGPU();
//    }else{
//        processByCPU();
//    }

    //  // 示例代码 只进行了简单FFT变换 和 转置
    processByCPU();
}

void CustomCnvToImgAlgorithmPlugin::checkParamters()
{

}

void CustomCnvToImgAlgorithmPlugin::initCudaAccLib()
{
    if(!getIsCudaAccLibOK()){
        bool isCudaAccLibOK=false;
        int devcnt;
        int gpu_device_id=-1;
        devcnt=getCudaDeviceCount();
        if(devcnt<1){
            emit toastInfo(tr("opencv has no cuda support."),"red");
            isCudaAccLibOK=false;
        }else{
            _CUDA_DEV_INFO *pCudaInfo;
            for (int i=0;i<devcnt;i++) {
                pCudaInfo=getCudaDeviceInfo(i);
                if(pCudaInfo->deviceOverlap!=0){
                    gpu_device_id = i;
                    break;
                }
            }

            if(gpu_device_id!=-1){
                if(!setCudaDevTo(gpu_device_id)) gpu_device_id=-1;
            }

            if(gpu_device_id!=-1){

                QString str;
                str=QString(tr("GPU name=%1"));
                emit toastInfo(str.arg(pCudaInfo->name),"black");

                str=QString(tr("version=%1 .%2"));
                emit toastInfo(str.arg(pCudaInfo->major).arg(pCudaInfo->minor),"black");

                str=QString(tr("multiProcessorCount=%1"));
                emit toastInfo(str.arg(pCudaInfo->multiProcessorCount),"black");

                str=QString(tr("isCompatible=%1"));
                emit toastInfo(str.arg(pCudaInfo->deviceOverlap),"black");

                str=QString(tr("current cuda dev id=%1"));
                emit toastInfo(str.arg(gpu_device_id),"black");

                cudaNop();

                isCudaAccLibOK=true;
            }else{
                isCudaAccLibOK=false;
            }
        }

        setIsCudaAccLibOK(isCudaAccLibOK);
    }
}

void CustomCnvToImgAlgorithmPlugin::setDefaultInteralVar()
{

}

void CustomCnvToImgAlgorithmPlugin::processByCPU()
{
    if(!m_recvImgData.ptr || m_recvImgData.type!=BUF_DATA_TYPE_U16C1) return;
    cv::Mat src(m_recvImgData.rows,
                m_recvImgData.cols,
                CV_16UC1,
                m_recvImgData.ptr
                );

    cv::Mat padded;
    int m = src.rows; //getOptimalDFTSize(src.rows);
    int n = cv::getOptimalDFTSize(src.cols);

    copyMakeBorder(src, padded,
                   0, m - src.rows,
                   0, n - src.cols,
                   cv::BORDER_CONSTANT, cv::Scalar::all(0)
                   );
    cv::Mat planes[] = { cv::Mat_<float>(padded),
                         cv::Mat::zeros(padded.size(),
                         CV_32F) };
    cv::Mat complexI;
    cv::merge(planes, 2, complexI);
    cv::dft(complexI, complexI,cv::DFT_ROWS);
    cv::split(complexI, planes);
    cv::magnitude(planes[0], planes[1], planes[0]);
    cv::Mat magI = planes[0];
    magI += cv::Scalar::all(1);
    cv::log(magI, magI);
    magI = magI(cv::Rect( 0, 0, magI.cols/2, magI.rows));
    cv::Mat outMat = cv::Mat_<unsigned short>(magI);
    //normalize(magI, magI, 0, 1, CV_MINMAX);
    magI.convertTo(outMat, CV_16U,4000.0); //4000.0

//    cv::imshow("after fft",outMat);
//    cv::waitKey(1);

    if(m_resultImgData.cols==outMat.rows
            && m_resultImgData.rows==outMat.cols
            && outMat.data
            ){
        this->transpose16BitData(outMat.rows,outMat.cols,
                                 outMat.data,m_resultImgData.ptr
                                 );
        outMat=cv::Mat(m_resultImgData.rows,
                       m_resultImgData.cols,
                       CV_16UC1,
                       m_resultImgData.ptr
                       );
    }

//    cv::imshow("after transpose",outMat);
//    cv::waitKey(1);
}

void CustomCnvToImgAlgorithmPlugin::processByGPU()
{

}

void CustomCnvToImgAlgorithmPlugin::transpose16BitData(int rows, int cols, void *src, void *dst)
{
    if(
            rows<2 ||
            cols<2 ||
            !src
            ){
        return;
    }

    unsigned short *sptr=reinterpret_cast<unsigned short *>(src);
    unsigned short *dptr=nullptr;
    bool needTempBuf=false;

    if(dst==nullptr || src==dst){
        needTempBuf=true;
        dptr=new unsigned short[static_cast<size_t>(rows*cols*2)];
    }else{
        dptr=reinterpret_cast<unsigned short *>(dst);
    }

    for (int j = 0; j < rows; ++j) {
        for (int i = 0; i < cols; ++i) {
            dptr[i*rows+j]=sptr[j*cols+i];
        }
    }

    if(needTempBuf && dptr){
        StdMemCpy(sptr,dptr,static_cast<size_t>(cols*rows*2));
        delete [] dptr;
    }
}
